<?php
/**
 * SEF extension for Joomla! 1.5
 *
 * @author      $Author: shumisha $
 * @copyright   Yannick Gaultier - 2009-2010
 * @package     sh404SEF-15
 * @license     http://www.gnu.org/copyleft/gpl.html GNU/GPL
 * @version     $Id: sh_Net_URL.class.php 1275 2010-04-21 12:21:52Z silianacom-svn $
 */

// Security check to ensure this file is being included by a parent file.
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');

// Net/Url.php From the PEAR Library (http://pear.php.net/package/sh_Net_URL)
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2004, Richard Heyes                                |
// | All rights reserved.                                                  |
// |                                                                       |
// | Redistribution and use in source and binary forms, with or without    |
// | modification, are permitted provided that the following conditions    |
// | are met:                                                              |
// |                                                                       |
// | o Redistributions of source code must retain the above copyright      |
// |   notice, this list of conditions and the following disclaimer.       |
// | o Redistributions in binary form must reproduce the above copyright   |
// |   notice, this list of conditions and the following disclaimer in the |
// |   documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote      |
// |   products derived from this software without specific prior written  |
// |   permission.                                                         |
// |                                                                       |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
// |                                                                       |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard at php net>                            |
// +-----------------------------------------------------------------------+
//
// $Id: sh_Net_URL.class.php 1275 2010-04-21 12:21:52Z silianacom-svn $
//
// sh_Net_URL Class
class sh_Net_URL
{
  /**
   * Full url
   * @var string
   */
  var $url;
  /**
   * Protocol
   * @var string
   */
  var $protocol;
  /**
   * Username
   * @var string
   */
  var $username;
  /**
   * Password
   * @var string
   */
  var $password;
  /**
   * Host
   * @var string
   */
  var $host;
  /**
   * Port
   * @var integer
   */
  var $port;
  /**
   * Path
   * @var string
   */
  var $path;
  /**
   * Query string
   * @var array
   */
  var $querystring;
  /**
   * Anchor
   * @var string
   */
  var $anchor;
  /**
   * Whether to use []
   * @var bool
   */
  var $useBrackets;
  /**
   * PHP4 Constructor
   *
   * @see __construct()
   */
  function sh_Net_URL($url = null, $useBrackets = true)
  {
    $this->__construct($url, $useBrackets);
  }
  /**
   * PHP5 Constructor
   *
   * Parses the given url and stores the various parts
   * Defaults are used in certain cases
   *
   * @param string $url         Optional URL
   * @param bool   $useBrackets Whether to use square brackets when
   *                            multiple querystrings with the same name
   *                            exist
   */
  function __construct($url = null, $useBrackets = true)
  {
    $HTTP_SERVER_VARS  = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
    $this->useBrackets = $useBrackets;
    $this->url         = $url;
    $this->user        = '';
    $this->pass        = '';
    $this->host        = '';
    $this->port        = 80;
    $this->path        = '';
    $this->querystring = array();
    $this->anchor      = '';
    // Only use defaults if not an absolute URL given
    if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) {
      $this->protocol    = (isset ($HTTP_SERVER_VARS['HTTPS']) ?
      (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http') : 'http');
      /**
       * Figure out host/port
       */
      if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) {
        $host = $matches[1];
        if (!empty($matches[3])) {
          $port = $matches[3];
        } else {
          $port = $this->getStandardPort($this->protocol);
        }
      }
      $this->user        = '';
      $this->pass        = '';
      $this->host        = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
      $this->port        = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
      $this->path        = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
      $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
      $this->anchor      = '';
    }
    // Parse the url and store the various parts
    if (!empty($url)) {
      // replace &amp; with &, other wise parse_url will not work properly
      $url = str_replace( '&amp;', '&', $url);
      $urlinfo = parse_url($url);
      // Default querystring
      $this->querystring = array();
      foreach ($urlinfo as $key => $value) {
        switch ($key) {
          case 'scheme':
            $this->protocol = $value;
            $this->port     = $this->getStandardPort($value);
            break;
          case 'user':
          case 'pass':
          case 'host':
          case 'port':
            $this->$key = $value;
            break;
          case 'path':
            if ($value{0} == '/') {
              $this->path = $value;
            } else {
              $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
              $this->path = sprintf('%s/%s', $path, $value);
            }
            break;
          case 'query':
            $this->querystring = $this->_parseRawQueryString($value);
            break;
          case 'fragment':
            $this->anchor = $value;
            break;
        }
      }
    }
  }

  /**
   * Returns full url
   *
   * @return string Full url
   * @access public
   */
  function getURL()
  {
    $querystring = $this->getQueryString();
    $this->url = $this->protocol . '://'
    . $this->user . (!empty($this->pass) ? ':' : '')
    . $this->pass . (!empty($this->user) ? '@' : '')
    . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
    . $this->path
    . (!empty($querystring) ? '?' . $querystring : '')
    . (!empty($this->anchor) ? '#' . $this->anchor : '');
    return $this->url;
  }
  /**
   * Adds a querystring item
   *
   * @param  string $name       Name of item
   * @param  string $value      Value of item
   * @param  bool   $preencoded Whether value is urlencoded or not, default = not
   * @access public
   */
  function addQueryString($name, $value, $preencoded = false)
  {
    if ($preencoded) {
      $this->querystring[$name] = $value;
    } else {
      $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
    }
  }
  /**
   * Removes a querystring item
   *
   * @param  string $name Name of item
   * @access public
   */
  function removeQueryString($name)
  {
    if (isset($this->querystring[$name])) {
      unset($this->querystring[$name]);
    }
  }
  /**
   * Sets the querystring to literally what you supply
   *
   * @param  string $querystring The querystring data. Should be of the format foo=bar&x=y etc
   * @access public
   */
  function addRawQueryString($querystring)
  {
    $this->querystring = $this->_parseRawQueryString($querystring);
  }
  /**
   * Returns flat querystring
   *
   * @return string Querystring
   * @access public
   */
  function getQueryString()
  {
    static $argSeparator = null;

    if (is_null( $argSeparator)) {
      $argSeparator = ini_get('arg_separator.output');
    }

    if (!empty($this->querystring)) {
      foreach ($this->querystring as $name => $value) {
        if (is_array($value)) {
          foreach ($value as $k => $v) {
            $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
          }
        } elseif (!is_null($value)) {
          $querystring[] = $name . '=' . $value;
        } else {
          $querystring[] = $name;
        }
      }
      $querystring = implode( $argSeparator, $querystring);
    } else {
      $querystring = '';
    }
    return $querystring;
  }

  function getQueryVars( $var = null) {

    if (is_null( $var)) {
      return $this->querystring;
    }

    $response = null;
    if (!empty($this->querystring)) {
      if (isset( $this->querystring[$var])) {
        $response = $this->querystring[$var];
      }
    }

    return $response;
  }

  /**
   * Parses raw querystring and returns an array of it
   *
   * @param  string  $querystring The querystring to parse
   * @return array                An array of the querystring data
   * @access private
   */

  function _parseRawQuerystring($querystring)
  {
    static $argSeparator = null;

    if (is_null( $argSeparator)) {
      $argSeparator = preg_quote(ini_get('arg_separator.input'), '/');
    }
    $parts  = preg_split('/[' . $argSeparator . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
    $return = array();
    foreach ($parts as $part) {
      if (strpos($part, '=') !== false) {
        $value = JString::substr($part, strpos($part, '=') + 1);
        $key   = JString::substr($part, 0, strpos($part, '='));
      } else {
        $value = null;
        $key   = $part;
      }
      if (JString::substr($key, -2) == '[]') {
        $key = JString::substr($key, 0, -2);
        if (@!is_array($return[$key])) {
          $return[$key]   = array();
          $return[$key][] = $value;
        } else {
          $return[$key][] = $value;
        }
      } elseif (!$this->useBrackets AND !empty($return[$key])) {
        $return[$key]   = (array)$return[$key];
        $return[$key][] = $value;
      } else {
        $return[$key] = $value;
      }
    }
    return $return;
  }
  /**
   * Resolves //, ../ and ./ from a path and returns
   * the result. Eg:
   *
   * /foo/bar/../boo.php    => /foo/boo.php
   * /foo/bar/../../boo.php => /boo.php
   * /foo/bar/.././/boo.php => /foo/boo.php
   *
   * This method can also be called statically.
   *
   * @param  string $url URL path to resolve
   * @return string      The result
   */
  function resolvePath($path)
  {
    $path = explode('/', str_replace('//', '/', $path));
    for ($i=0; $i<count($path); $i++) {
      if ($path[$i] == '.') {
        unset($path[$i]);
        $path = array_values($path);
        $i--;
      } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
        unset($path[$i]);
        unset($path[$i-1]);
        $path = array_values($path);
        $i -= 2;
      } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
        unset($path[$i]);
        $path = array_values($path);
        $i--;
      } else {
        continue;
      }
    }
    return implode('/', $path);
  }
  /**
   * Returns the standard port number for a protocol
   *
   * @param  string  $scheme The protocol to lookup
   * @return integer         Port number or NULL if no scheme matches
   *
   * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
   */
  function getStandardPort($scheme)
  {
    switch (strtolower($scheme)) {
      case 'http':    return 80;
      case 'https':   return 443;
      case 'ftp':     return 21;
      case 'imap':    return 143;
      case 'imaps':   return 993;
      case 'pop3':    return 110;
      case 'pop3s':   return 995;
      default:        return null;
    }
  }
  /**
   * Forces the URL to a particular protocol
   *
   * @param string  $protocol Protocol to force the URL to
   * @param integer $port     Optional port (standard port is used by default)
   */
  function setProtocol($protocol, $port = null)
  {
    $this->protocol = $protocol;
    $this->port = is_null($port) ? $this->getStandardPort() : $port;
  }
}
